Skip to content

fix(streaming): reject Fin with out-of-range sequence instead of overflowing#145

Open
Jr-kenny wants to merge 1 commit into
circlefin:mainfrom
Jr-kenny:fix/streaming-fin-sequence-overflow
Open

fix(streaming): reject Fin with out-of-range sequence instead of overflowing#145
Jr-kenny wants to merge 1 commit into
circlefin:mainfrom
Jr-kenny:fix/streaming-fin-sequence-overflow

Conversation

@Jr-kenny

Copy link
Copy Markdown

Fixes #144.

What's going on

StreamState::insert in crates/malachite-app/src/streaming.rs works out how many parts a stream should have from the Fin message's sequence:

self.expected_messages = msg.sequence as usize + 1;

The comment above it says the + 1 can't overflow because of MAX_MESSAGES_PER_STREAM, but expected_messages is taken from the Fin sequence, which is a wire field the sending peer controls, not from the message cap. A single gossiped Fin at sequence = u64::MAX reaches that line unchecked (received_proposal_part.rs:163 hands the raw message straight to streams_map.insert), and u64::MAX as usize + 1 overflows: a panic in debug or debug-assertions builds, and in release it wraps instead, so the stream can never complete and just sits on a per-peer slot until the 60s age sweep clears it.

Fix

A complete stream holds fin.sequence + 1 messages and can never hold more than MAX_MESSAGES_PER_STREAM, so a Fin with sequence >= MAX_MESSAGES_PER_STREAM is describing a stream that can never complete. I reject and evict it up front, the same way the other malformed-stream paths already do (new StreamInsertResult::FinSequenceOutOfRange), which also takes the overflow off the table. The misleading comment is corrected while I'm there.

Testing

Added test_fin_with_out_of_range_sequence_is_rejected_without_overflow, covering the u64::MAX case and the MAX_MESSAGES_PER_STREAM boundary. Both now return Pending with no stream left tracked, instead of panicking. The rest of the streaming suite stays green:

cargo test -p arc-node-consensus --lib streaming::
test result: ok. 47 passed; 0 failed; 0 ignored

cargo clippy -p arc-node-consensus --lib is clean too.

…flowing

A Fin proposal-part carries a peer-controlled sequence, and StreamState::insert
turned it straight into expected_messages with `msg.sequence as usize + 1`. The
comment there claimed that could not overflow because of MAX_MESSAGES_PER_STREAM,
but expected_messages comes from the Fin sequence, not the message cap, so a Fin
at u64::MAX overflowed and panicked in debug builds (and wedged the stream in
release until the age sweep).

A complete stream can never hold more than MAX_MESSAGES_PER_STREAM messages, so a
Fin whose sequence is >= MAX_MESSAGES_PER_STREAM describes a stream that can never
complete. Reject and evict it up front like the other malformed-stream paths,
which also removes the overflow. Corrects the misleading comment and adds a
regression test for the u64::MAX and boundary cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: unvalidated Fin proposal-part sequence overflows expected_messages (panics in overflow-checked node builds, wedges the stream in release)

1 participant